package com.sca.auth.utils;

import com.sca.auth.constant.RedisKeyConstant;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.util.*;


/**
 * @author liuxianfei
 * @since 2020/9/19 15:17
 */
@Component
public class JwtUtil {

    /**
     * Token签名
     */
    private static final String SECRET = "4645313f48284d60af352d548d0d78f9";

    /**
     * Token前缀
     */
    public static final String TOKEN_PREFIX = "Bearer ";

    /**
     * 自定义头使用小写，兼容http2
     */
    public static final String HEADER_AUTHORIZATION = "authorization";

    /**
     * 当前登录用户Id
     */
    public static final String USER_ID = "userId";
    public static final String UUID_STR = "uuid";

    private final RedisTemplate<String, Object> redisTemplate;
    private static RedisTemplate<String, Object> staticRedisTemplate;

    @Autowired
    public JwtUtil(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @PostConstruct
    public void init() {
        staticRedisTemplate = redisTemplate;
    }


    /**
     * 生成用户token
     *
     * @param userId 用户id
     * @return token字符串
     */
    public static String generateToken(Integer userId) {
        String jwt = Jwts.builder()
            .claim(USER_ID, userId)
            .claim(UUID_STR, generate())
            .signWith(SignatureAlgorithm.HS512, SECRET)
            .compact();
        return TOKEN_PREFIX + jwt;
    }

    public static String generate() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 解析token
     *
     * @param request 请求对象
     * @return true/false
     */
    public static Boolean validateToken(HttpServletRequest request, HttpServletResponse response) {
        String token = request.getHeader(HEADER_AUTHORIZATION);
        if (token != null && token.startsWith(TOKEN_PREFIX)) {
            Claims body = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody();
            Integer userId = body.get(USER_ID, Integer.class);
            Object userToken = staticRedisTemplate.opsForValue().get(RedisKeyConstant.AUTH_STR_USER_TOKEN + userId);
            if (userToken == null || StringUtils.isEmpty(userToken)) {
                return false;
            }
            return token.equals(userToken);
        }
        return false;
    }

    public static class CustomHttpServletRequest extends HttpServletRequestWrapper {
        private final Map<String, String> claimsMap;

        CustomHttpServletRequest(HttpServletRequest request, Claims claims) {
            super(request);
            claimsMap = new HashMap<>(claims.size());
            claims.forEach((key, value) -> claimsMap.put(key, String.valueOf(value)));
        }

        @Override
        public Enumeration<String> getHeaders(String name) {
            if (claimsMap != null && claimsMap.containsKey(name)) {
                return Collections.enumeration(Collections.singletonList(claimsMap.get(name)));
            }
            return super.getHeaders(name);
        }

        @Override
        public String getHeader(String name) {
            if (claimsMap != null && claimsMap.containsKey(name)) {
                return claimsMap.get(name);
            }
            return super.getHeader(name);
        }
    }

    /**
     * 解析token到请求头
     */
    public static HttpServletRequest appendRequestHeader(HttpServletRequest request, HttpServletResponse response) {
        String token = request.getHeader(HEADER_AUTHORIZATION);
        if (token != null && token.startsWith(TOKEN_PREFIX)) {
            String tokenContent = token.replace(TOKEN_PREFIX, "");
            Claims body = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(tokenContent).getBody();
            return new CustomHttpServletRequest(request, body);
        }
        return request;
    }

}
